在安卓上使用ML Kit识别图像中的文本

您可以使用ML Kit来识别图像中的文本,使用设备上的模型或云上的模型。请参阅概述以了解每种方法的优点。

有关此API使用的示例,请参阅GitHub上的ML Kit快速入门示例,或者尝试使用codelab

在您开始之前

  1. 如果您还没有将Firebase添加到您的程序当中,那您可以从开始指南来开始您的工作。

  2. 在app-level的build.gradle 文件中为ML kit添加依赖:

    1. dependencies {
    2. // ...
    3. implementation 'com.google.firebase:firebase-ml-vision:15.0.0'
    4. }
  3. 可选但建议:如果您使用设备上的API,请将应用配置为在从Play商店安装应用后自动将ML模型下载到设备:

    1. <meta-data
    2. android:name="com.google.firebase.ml.vision.DEPENDENCIES"
    3. android:value="text" />
    4. <!-- 为了能够使用多个模型: android:value="text,model2,model3" -->

    如果您未启用安装时模型下载,则将在您首次运行设备上识别器的时候开始下载该模型。在下载完成之前发出的请求不会有任何结果。

  4. 如果您想使用基于云的模型,并且尚未将项目升级到Blaze计划,请在Firebase控制台中执行此操作。只有Blaze计划的项目才能使用Cloud Vision API。

  5. 如果您想使用基于云的模型,您也需要开启Cloud Vision API:

    • 在云API列表管理平台中打开Cloud Vision API
    • 确保您的Firebase项目已经在当前菜单页面中被置于顶端。
    • 如果API依旧还是显示为enabled,请点击Enable。

    如果您想要仅仅开启使用设备上的模型,您可以跳过这一步。

现在您已经可以开始使用设备上的模型或者基于云端的模型识别图像中的文本了。

在设备上识别文本

为了使用设备上的文本识别模型,请运行如下文本识别器。

  1. 运行文本识别器

    为了识别图像中的文本,从任一个Bitmapmedia.ImageByteBuffer,字节阵列,或在设备上的文件中创建一个FirebaseVisionImage对象。然后,传递FirebaseVisionImage对象到FirebaseVisionTextDetectordetectInImage方法。

    1. 从图片中创建FirebaseVisionImage对象.

      • Bitmap对象创建FirebaseVisionImage对象:

        1. FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

        Bitmap 对象指示的图像必须是直立的,不能旋转。

      • 要从 media.Image对象创建FirebaseVisionImage对象(例如从设备的相机捕捉图像时),首先要确定图像必须旋转的角度,以补偿设备的旋转和相机传感器在设备中的方向:

        1. private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
        2. static {
        3. ORIENTATIONS.append(Surface.ROTATION_0, 90);
        4. ORIENTATIONS.append(Surface.ROTATION_90, 0);
        5. ORIENTATIONS.append(Surface.ROTATION_180, 270);
        6. ORIENTATIONS.append(Surface.ROTATION_270, 180);
        7. }
        8. /**
        9. * 得到当前图像需要补偿的角度
        10. */
        11. private int getRotationCompensation(Activity activity, String cameraId)
        12. throws CameraAccessException {
        13. // 得到设备当前与原始的角度的旋转差值
        14. // 然后照片一定要旋转回去相对的差值
        15. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        16. int rotationCompensation = ORIENTATIONS.get(deviceRotation);
        17. // 在大多数的设备上,传感器的方向是90度。但是对于
        18. // 少数设备,这个值是270度。那么对于这些270度的设备
        19. // 必须让照片旋转额外的180 ((270 + 270) % 360) 度.
        20. CameraManager cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
        21. int sensorOrientation = cameraManager
        22. .getCameraCharacteristics(cameraId)
        23. .get(CameraCharacteristics.SENSOR_ORIENTATION);
        24. rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
        25. // 返回相关的FirebaseVisionImageMetadata rotation值。
        26. int result;
        27. switch (rotationCompensation) {
        28. case 0:
        29. result = FirebaseVisionImageMetadata.ROTATION_0;
        30. break;
        31. case 90:
        32. result = FirebaseVisionImageMetadata.ROTATION_90;
        33. break;
        34. case 180:
        35. result = FirebaseVisionImageMetadata.ROTATION_180;
        36. break;
        37. case 270:
        38. result = FirebaseVisionImageMetadata.ROTATION_270;
        39. break;
        40. default:
        41. result = FirebaseVisionImageMetadata.ROTATION_0;
        42. Log.e(TAG, "Bad rotation value: " + rotationCompensation);
        43. }
        44. return result;
        45. }
        46. ...
        47. // 得到正在使用CameraManager的camera的ID。随后:
        48. int rotation = getRotationCompensation(this, MY_CAMERA_ID);

        然后,将media.Image对象和旋转值传递给FirebaseVisionImage.fromMediaImage()

        1. FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage,rotation);
      • 要从一个字节数组或ByteBuffer创建一个FirebaseVisionImage对象,首先按照上面的描述计算图像旋转角度。

        然后,创建一个包含图像高度,宽度,颜色编码格式和旋转度的FirebaseVisionImageMetadata对象:

        1. FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
        2. .setWidth(1280)
        3. .setHeight(720)
        4. .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
        5. .setRotation(rotation)
        6. .build();

        使用缓冲区或数组以及元数据对象来创建一个 FirebaseVisionImage对象:

        1. FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
        2. // 或者: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
      • 要从文件创建FirebaseVisionImage对象,请将应用context和文件URI传递给FirebaseVisionImage.fromFilePath()

        1. FirebaseVisionImage image;
        2. try {
        3. image = FirebaseVisionImage.fromFilePath(context, uri);
        4. } catch (IOException e) {
        5. e.printStackTrace();
        6. }
    2. 获取一个FirebaseVisionTextDetector实例:

      1. FirebaseVisionTextDetector detector = FirebaseVision.getInstance()
      2. .getVisionTextDetector();
    3. 最后,将图像传递给detectInImage方法:

      1. Task<FirebaseVisionText> result =
      2. detector.detectInImage(image)
      3. .addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() {
      4. @Override
      5. public void onSuccess(FirebaseVisionText firebaseVisionText) {
      6. // 任务成功完成
      7. // ...
      8. }
      9. })
      10. .addOnFailureListener(
      11. new OnFailureListener() {
      12. @Override
      13. public void onFailure(@NonNull Exception e) {
      14. // 任务失败并且报异常
      15. // ...
      16. }
      17. });
  2. 从识别的文本块中提取文本

    如果文本识别操作成功,程序则会将FirebaseVisionTextBlock的数组传递给成功侦听器(success listener)。每个FirebaseVisionTextBlock对象代表在图像中识别到的文本的矩形块(例如一段打印文本)。对于每一个FirebaseVisionTextBlock,您都可以获取块的边界坐标和块中包含的文本。另外,对于FirebaseVisionTextBlock,您都可以获得组成该块的文本行,以及构成每行文本的元素(如字符或标点符号):

    1. for (FirebaseVisionText.Block block: firebaseVisionText.getBlocks()) {
    2. Rect boundingBox = block.getBoundingBox();
    3. Point[] cornerPoints = block.getCornerPoints();
    4. String text = block.getText();
    5. for (FirebaseVisionText.Line line: block.getLines()) {
    6. // ...
    7. for (FirebaseVisionText.Element element: line.getElements()) {
    8. // ...
    9. }
    10. }
    11. }

基于云端的文本识别

要使用基于云的文本识别模型,请配置并运行文本检测器,如下所述。

  1. 配置文本检测器

    默认情况下,云识别器会使用模型的稳定版本并返回多达10个结果。如果要更改这些设置中的任何一个,请用FirebaseVisionCloudDetectorOptions 对象指定它们。

    例如,要更改这些默认设置,按照以下示例构建一个FirebaseVisionCloudDetectorOptions对象:

    1. FirebaseVisionCloudDetectorOptions options =
    2. new FirebaseVisionCloudDetectorOptions.Builder()
    3. .setModelType(FirebaseVisionCloudDetectorOptions.LATEST_MODEL)
    4. .setMaxResults(15)
    5. .build();

    要使用默认设置,可以在下一步中使用FirebaseVisionCloudDetectorOptions.DEFAULT

  2. 运行文本检测器

    为了识别图像中的文本,从任一个Bitmapmedia.ImageByteBuffer或者字节阵列创建一个FirebaseVisionImage对象,抑或在设备上的文件中选取一个。然后,传递FirebaseVisionImage对象到无论是FirebaseVisionCloudTextDetector或(如果图像是一个文件的话)FirebaseVisionCloudDocumentTextDetectordetectInImage 方法。

    1. 从您的图像中创建一个FirebaseVisionImage对象。

      • Bitmap对象创建FirebaseVisionImage对象:

        1. FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

        Bitmap物体表示的图像必须是直立的,不需要旋转。

      • 要从media.Image对象创建FirebaseVisionImage对象(例如从设备的相机捕捉图像时),首先要确定图像必须旋转的角度,以补偿设备的旋转和相机传感器在设备中的方向差:

        1. private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
        2. static {
        3. ORIENTATIONS.append(Surface.ROTATION_0, 90);
        4. ORIENTATIONS.append(Surface.ROTATION_90, 0);
        5. ORIENTATIONS.append(Surface.ROTATION_180, 270);
        6. ORIENTATIONS.append(Surface.ROTATION_270, 180);
        7. }
        8. /**
        9. * 得到当前图像需要补偿的角度
        10. */
        11. private int getRotationCompensation(Activity activity, String cameraId)
        12. throws CameraAccessException {
        13. // 得到设备当前与原始的角度的旋转差值
        14. // 然后照片一定要旋转回去相对的差值
        15. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        16. int rotationCompensation = ORIENTATIONS.get(deviceRotation);
        17. // 在大多数的设备上,传感器的方向是90度。但是对于
        18. // 少数设备,这个值是270度。那么对于这些270度的设备
        19. // 必须让照片旋转额外的180 ((270 + 270) % 360) 度.
        20. CameraManager cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
        21. int sensorOrientation = cameraManager
        22. .getCameraCharacteristics(cameraId)
        23. .get(CameraCharacteristics.SENSOR_ORIENTATION);
        24. rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
        25. // 返回相关的FirebaseVisionImageMetadata的旋转值。
        26. int result;
        27. switch (rotationCompensation) {
        28. case 0:
        29. result = FirebaseVisionImageMetadata.ROTATION_0;
        30. break;
        31. case 90:
        32. result = FirebaseVisionImageMetadata.ROTATION_90;
        33. break;
        34. case 180:
        35. result = FirebaseVisionImageMetadata.ROTATION_180;
        36. break;
        37. case 270:
        38. result = FirebaseVisionImageMetadata.ROTATION_270;
        39. break;
        40. default:
        41. result = FirebaseVisionImageMetadata.ROTATION_0;
        42. Log.e(TAG, "Bad rotation value: " + rotationCompensation);
        43. }
        44. return result;
        45. }
        46. ...
        47. // 得到正在使用CameraManager的camera的ID。随后:
        48. int rotation = getRotationCompensation(this, MY_CAMERA_ID);

        然后,将media.Image对象和旋转值传递给FirebaseVisionImage.fromMediaImage()

        1. FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
      • 要从一个字节数组或ByteBuffer创建一个FirebaseVisionImage对象 ,首先按照上面的描述计算图像旋转。

        然后,创建一个包含图像高度,宽度,颜色编码格式和旋转的FirebaseVisionImageMetadata对象:

        1. FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
        2. .setWidth(1280)
        3. .setHeight(720)
        4. .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
        5. .setRotation(rotation)
        6. .build();

        使用buffer或数组以及元数据对象来创建一个 FirebaseVisionImage对象:

        1. FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
        2. // 或者:
        3. FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(array, metadata);
      • 要从文件创建FirebaseVisionImage对象,请将应用context和文件URI传递给FirebaseVisionImage.fromFilePath()

        1. FirebaseVisionImage image;
        2. try {
        3. image = FirebaseVisionImage.fromFilePath(context, uri);
        4. } catch (IOException e) {
        5. e.printStackTrace();
        6. }
    2. 获取FirebaseVisionCloudTextDetectorFirebaseVisionCloudDocumentTextDetector的一个实例:

      1. FirebaseVisionCloudTextDetector detector = FirebaseVision.getInstance()
      2. .getVisionCloudTextDetector();
      3. // 或者,修改默认设定:
      4. // FirebaseVisionCloudTextDetector detector = FirebaseVision.getInstance()
      5. // .getVisionCloudTextDetector(options);
    3. 最后,将图像传递给detectInImage方法:

      1. Task<FirebaseVisionCloudText> result = detector.detectInImage(image)
      2. .addOnSuccessListener(new OnSuccessListener<FirebaseVisionCloudText>() {
      3. @Override
      4. public void onSuccess(FirebaseVisionCloudText firebaseVisionCloudText) {
      5. // 任务成功完成
      6. // ...
      7. }
      8. })
      9. .addOnFailureListener(new OnFailureListener() {
      10. @Override
      11. public void onFailure(@NonNull Exception e) {
      12. // 任务失败并发出异常
      13. // ...
      14. }
      15. });

3.从识别的文本块中提取文本

如果文本识别操作成功,则会将FirebaseVisionCloudText对象传递给成功侦听器(success listener)。该对象包含图像中识别的文本。

您还可以获取有关文本结构的信息。文本被组织成页面,块,段落,单词和符号。对于组织成的每个单位,您都可以获取其详细信息,例如其维度及其包含的语言。

例如:

  1. String recognizedText = firebaseVisionCloudText.getText();
  2. for (FirebaseVisionCloudText.Page page: firebaseVisionCloudText.getPages()) {
  3. List<FirebaseVisionCloudText.DetectedLanguage> languages =
  4. page.getTextProperty().getDetectedLanguages();
  5. int height = page.getHeight();
  6. int width = page.getWidth();
  7. float confidence = page.getConfidence();
  8. for (FirebaseVisionCloudText.Block block: page.getBlocks()) {
  9. Rect boundingBox = block.getBoundingBox();
  10. List<FirebaseVisionCloudText.DetectedLanguage> blockLanguages =
  11. block.getTextProperty().getDetectedLanguages();
  12. float blockConfidence = block.getConfidence();
  13. // And so on: Paragraph, Word, Symbol
  14. }
  15. }